/*
泛型的使用
泛型类的定义，实际上就是比普通的类多了一个类型参数，定义在类上的参数。也就是在使用时需要指定具体的泛型类型。
泛型的名称一般取首个大写字母，比如T代表type，当然也可以添加数字和其他字符，还有E，K，V等
 */
package com.Generics2;

public class Score<N, I ,T> {  //将Score类转为泛型类
    //泛型有几个定义几个
    N name;  //N为泛型，根据用户提供的类型自动变成对应类型
    I id;
    T score;

    public Score(N name, I id, T score){
        this.name = name;
        this.id = id;
        this.score = score;
    }

    /*
        泛型方法
    泛型方法的使用也很简单，只需要把它当做一个未知的类型来使用即可
         */
    public T getScore(){  //若方法的返回值类型为泛型，编译器会自动推断
        return score;
    }

    public void setScore(T score){  //如果方法的形参是泛型，那么实参只能是定义时的类型
        this.score = score;
    }

    //静态方法定义泛型变量
    public static <E> void test(E e){  //不像类必须创建对象才能明确类型，在静态方法定义前声明泛型，直接在方法里面使用这个泛型，就能明确类型
        System.out.println(e);
    }

    //成员方法也可以定义泛型变量
    public <E> void test2(E e){
        System.out.println(e);
    }


    public static void main(String[] args) {
        /*
        在一个普通类型中定义泛型，泛型T成为参数化类型，在定义泛型类的引用时，需要明确指出类型
        其中new Score<>中的类型可以省略
         */
        Score<String, Integer, String> score1 = new Score<String, Integer, String>("数学", 12345, "优秀");
        //此时类中的泛型N、I、T已经被替换为对应的类型了，在我们获取此对象的泛型属性时，编译器会直接告诉我们类型，此时再进行类型转换，编译器会直接报错不通过
        /*
        泛型的两个基本特性
        1、静态的成员变量不能使用,注意泛型只能用于对象属性，因为static是类具有的属性，直接类.xx用就可以了，泛型是在创建对象的时候才确定类型
        static T Score;  这行会报错
        泛型是只有在创建对象后编译器才能明确泛型类型，而静态类型是类所具有的属性，不足以使得编译器完成类型推断
        同样的，静态方法无法直接使用类定义的泛型，注意是无法直接使用。
        2、泛型不能用于基本类型，如果要使用基本类型，只能使用基本类型的包装类进行替换
        Score<String, int, int> score2 = new Score<String, int, String>("物理", 123, 80);
        这个创建对象的语句会报错 Type argument cannot be of primitive type 类型参数不能是原始类型
        因为泛型本质最后还是转成Object了，任意类型直接或间接引用Object，而基本类型不是类，不能转换为引用类型，不是引用Object。
         */
    }
}

//其实无论是泛型类还是泛型方法，使用时一定要能够进行类型推断，明确类型就可以。注意区分类定义的泛型和方法前定义的泛型
